from commands import *
from google.appengine.api import memcache, users, xmpp
from google.appengine.ext import webapp
from lang import *
import models
import debug
import time

class MessageProcessError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return self.value

class Member(models.GroupMember):
    """ The class of every group member.
    """
    def __init__(self, entity, jid=None):
        """ Create a new instance of a member.
            entity:   a member's entity,if it is set,the new instance will be binded to it
            jid:      a valid jid,used for new member
        """
        if not jid is None:
            # new instance of a jid
            self.jid = jid

        else:
            # copy all attributes of entity to current instance
            self.update(entity)
        # get member status
        self.status = memcache.get(entity.jid, "MemberStatus")
        if self.status is None:
            self.status = {'muted':False,
                          'muted_time':int(time.time())
                         }

    def update(self, entity):
        for k,v in entity.__dict__:
            if not str.startswith(k, '_'):
                self.__dict__[k] = v
 
    def can_recv_from(self, sender):
        """ Whether a member can receive messages
        Returns: True,can receive messages,otherwise False
        """
        blocked = self.sender.jid in self.sender.blocklist
        return not blocked
    def register(self):
        """ Create a new member entity in the data store."""
        try:
            new_member = models.GroupMember()
            new_member.jid = self.jid
            new_member.account = users.User(new_member.jid)
            new_member.nick = new_member.account.nickname()
            new_member.warning = 0
            new_member.alwaysrecev = False
            new_member.blocklist = []
            new_member.lastspeek = time.time()
            new_member.permission = models.DefaultMemberPermission()
        except users.UserNotFoundError as e:
            logging.warning("%s" % e)
        new_member.put()
        

class MessageProcessor(xmpp.Message):
    """ Process all messages.
    message:   a xmpp.Message object,contains the jids of senders and the message body
    Returns None
    """
    def __int__(self, message):
        """ Create a MessageProcessor instance.
            message: the message intend to process
        """
        # initialize the default variables
        super(xmpp.Message, self).__init__(message)
        self.body = self.decode(message)
        self.nickl = config.chat['nick_left']
        self.nickr = config.chat['nick_right']
        self.bot_name = self.nickl + config.bot['nick']
        # obtain the list of all Member objects
        self.get_lists(self.message)

    def process(self):
        # if sender is blocked
        if self.sender in self.block_list:
            self.reply(tr("Sorry,you are currently blocked by group,contact one of the group operators to unblock."))
            logging.info("Blocked user %s's message received." % self.sender.jid)
        #try to process the message as a command message
        try:
            command = CommandProcessor(self)
            command.run()
            return
        except NotACommand:
            pass
        except CommandNotValid as e:
            # write error message to logging
            if config.bot['debug']:
                logging.warning(e)
            
            command = CommandProcessor(self)
            command.run('/help')
            return
        # if sender is muted
        time_left = int(time.time()) - self.sender.status['muted_time']
        if self.sender.status['muted'] and time_left > 0:
            self.reply(tr('Sorry,you are currently muted.You have %s left.' % time_left))
            return
        else:
            self.sender.status['muted'] = False
        
        self.broadcast(self.body)
    
    def wrap_msg(self,msg,client):
        return self.nickl+client.nickname+self.nickr+config.chat['msg_separator']+msg
    
    def broadcast(self,msg,sender_jid):
        try:
            for client in self.online_list:
                if client.can_recv_from(sender_jid):
                    xmpp.send_message(client.jid,self.wrap_msg(msg,client))
        except Exception as ex:
            logging.error(
                        'Error occured in %s when broadcasting message from %s.' 
                          % (debug.cur_function(), client.jid))
        
    def build_member_list(self):
        """ Build instance list of class Member using memcache."""
        all_members = models.GroupMember.all()
        self.member_list = []
        self.online_list = []
        for member in all_members:
            self.member_list.append(Member(member))
            if xmpp.get_presence(member.jid):
                self.online_list.append(member)
        try:
            memcache.set_mult({'member_list':self.member_list,
                               'online_list':self.online_list})
        except ValueError as e:
            logging.error("Memcache in %s: %s", debug.cur_function(), e)
        
    def decode(self, message):
        """ Try to decode the message body using default encoding. If failed then send error message to the sender."""
        msg = message.body
        try:
            msg = str.decode(message.body)
        except UnicodeDecodeError:
            message.body.reply(tr('It seems that your not using %s encoding,change your encoding setting to that or your messages will not be delivered.' 
                                  % config.chat['encoding']))
            raise MessageProcessError
        return msg
    
    def get_lists(self, message):
        """ Get the reference of the member_list and online_list using memcache."""
        # get member list
        self.member_list = memcache.get("member_list")
        self.online_list = memcache.get("online_list")
        if self.member_list is None or self.online_list is None:
            self.build_member_list()
        # get sender instance of Member
        for client in self.member_list:
            if client.email == message.sender:
                self.sender = client
                break
        #not in the list,must be a new member
        #the client is a new member or a blocked user
        if self.sender is None:
            self.sender = Member(str.split(message.sender, '/')[0])
            self.sender.register()
  

